﻿namespace EyeSoft.AutoMapper
{
	using System;
	using System.Linq;
	using System.Reflection;

	using global::AutoMapper.QueryableExtensions;

	using EyeSoft.Mapping;

	public class AutoMapperProjection : IProjection
	{
		private static readonly MethodInfo enumerableCastMethod =
			typeof(Enumerable).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public);

		private static readonly Type projectExpressionType =
			typeof(ProjectionExpression<>);

		public IQueryable<TResult> Project<TResult>(IQueryable source)
		{
			var sourceConverted =
				enumerableCastMethod
					.MakeGenericMethod(source.ElementType)
					.Invoke(null, new object[] { source });

			var genericProjectExpressionType =
				projectExpressionType.MakeGenericType(source.ElementType);

			var projectExpression =
				genericProjectExpressionType
				.CreateInstance(new[] { sourceConverted, global::AutoMapper.Mapper.Engine });

			var projectExpressionTypeToMethod =
				genericProjectExpressionType.GetMethod("To", BindingFlags.Instance | BindingFlags.Public);

			var projectTo =
				projectExpressionTypeToMethod
					.MakeGenericMethod(typeof(TResult));

			var result = (IQueryable<TResult>)projectTo.Invoke(projectExpression, null);

			return result;
		}
	}
}